# Fusion File Abstraction Layer
# for PyFileServer

from fusion.db.db import DB
from fusion.db.exceptions import *
import sys, os, StringIO, time, md5, re

from pyfileserver.processrequesterrorhandler import HTTPRequestException
import pyfileserver.processrequesterrorhandler

class FusionFileAbstractionLayer:

    dbfile = '~/.fusion/fusion.db'

    def resolvePath(self, resheadpath, urlelementlist):
        path = '/'.join(urlelementlist)
        return resheadpath + '/' + path

    def breakPath(self, resheadpath, respath):
        path = respath.strip('/')
        return path.split('/')
        
    def getResourceDescriptor(self, respath):
        desc = self.getResourceDescription(respath)
        size = str(self.getContentLength(respath)) + " B"
        mod = self.getLastModified(respath)
        return [desc, size, mod]

    def getResourceDescription(self, respath):
        db = DB(self.dbfile)

        if respath is '':
            db.close()
            return 'Directory'

        respath = respath.strip('/')

        # 01230123019adf../info/title -> value

        path = respath.split('/')
        col = path[0]
        path = path[1:len(path)]
        path = '/'.join(path)

        if path is '' and col in db.list_collections():
            db.close()
            return 'Directory' # Easy case
        
        if db.get(col, path) is not None:
            db.close()
            return 'File'
            
        if len(db.keys(col, path)) > 0:
            db.close()
            return 'Directory'

        db.close()
        return 'Unknown'


    def getContentType(self, respath):
        if self.getResourceDescription(respath) is 'Directory':
            return 'text/html'
        else:
            return 'text/plain'

    def getLastModified(self, respath):
        at_eight_nov = 1194513353.316092
        #return str(time.time())
        return str(at_eight_nov)

    def getContentLength(self, respath):
        db = DB(self.dbfile)

        if respath is '':
            db.close()
            return 0

        respath = respath.strip('/')

        path = respath.split('/')
        col = path[0]
        path = '/'.join(path[1:len(path)])
        tmplen = db.get(col, path)
        if tmplen is None:
            length = 0
        else:
            length = len(tmplen)
        db.close()
        return length

    def getEntityTag(self, respath):
        return md5.new(respath).hexdigest() + '-' + str(self.getLastModified(respath)) + '-' + str(self.getContentLength(respath))

    def isCollection(self, respath):
        db = DB(self.dbfile)

        if respath is '':
            db.close()
            return True

        respath = respath.strip('/')

        path = respath.split('/')
        col = path[0]
        path = '/'.join(path[1:len(path)])

        if col in db.list_collections() and path is '':
            db.close()
            return True

        if db.get(col, path) is not None:
            db.close()
            return False

        if len(db.keys(col, path)) > 0:
            db.close()
            return True

        db.close()
        return False

    def isResource(self, respath):
        db = DB(self.dbfile)

        if respath is '':
            db.close()
            return False

        respath = respath.strip('/')

        path = respath.split('/')
        col = path[0]
        path = '/'.join(path[1:len(path)])
        
        if col in db.list_collections() and path is '':
            db.close()
            return False

        if db.get(col, path) is not None:
            db.close()
            return True
        else:
            db.close()
            return False


    def exists(self, respath):
        db = DB(self.dbfile)

        if respath is '':
            db.close()
            return True

        respath = respath.strip('/')
        path = respath.split('/')
        col = path[0]
        path = path[1:len(path)]
        path = '/'.join(path)

        if col in db.list_collections() and path is '':
            db.close()
            return True

        if db.get(col, path) is not None:
            db.close()
            return True

        if len(db.keys(col, path)) > 0:
            db.close()
            return True

        db.close()
        return False

    def createCollection(self, respath):
        raise HTTPRequestException(processrequesterrorhandler.HTTP_FORBIDDEN)

    def deleteCollection(self, respath):
        raise HTTPRequestException(processrequesterrorhandler.HTTP_FORBIDDEN)

    def supportEntityTag(self, respath):
        return True

    def supportLastModified(self, respath):
        return True

    def supportContentLength(self, respath):
        return True

    def supportRanges(self, respath):
        return False

    def openResourceForRead(self, respath):
        db = DB(self.dbfile)
        
        respath = respath.strip('/')
        
        path = respath.split('/')
        col = path[0]

        path = path[1:len(path)]
        path = '/'.join(path)
        res = db.get(col, path)
        db.close()
        return StringIO.StringIO(res)

    def openResourceForWrite(self, respath, contenttype=None):

        db = DB(self.dbfile)

        respath = respath.strip('/')

        path = respath.split('/')
        col = path[0]
        path = path[1:len(path)]
        path = '/'.join(path)       

        val = db.get(col, path)

        ffile = Fusbuf(val, respath)

        db.close()

        return ffile

    def deleteResource(self, respath):
        db = DB(self.dbfile)

        respath = respath.strip('/')

        path = respath.split('/')
        col = path[0]
        path = path[1:len(path)]
        path = '/'.join(path)       

        try:
            db.put(col, path, '')
        except Unauthorized:
            raise HTTPRequestException(processrequesterrorhandler.HTTP_FORBIDDEN) 

        finally:
            db.close()
        
    def copyResource(self, respath, destrespath):
        raise HTTPRequestException(processrequesterrorhandler.HTTP_FORBIDDEN)

    def getContainingCollection(self, respath):
        path = respath.split('/')
        path = path[0:len(path)-1]
        return '/'.join(path)

    def getCollectionContents(self, respath):
        db = DB(self.dbfile)
        
        if respath is '':
            cols = db.list_collections()
            db.close()
            return cols

        respath = respath.strip('/')

        path = respath.split('/')
        col = path[0]
        path = path[1:len(path)]
        path = '/'.join(path)
        kiis = db.keys(col, path)
        new_keys = []
        for key in kiis:
            new_keys.append(key.split('/')[0])
        key_set = set(new_keys)
        db.close()
        return key_set

    def joinPath(self, rescollectionpath, resname):
        return rescollectionpath + '/' + resname

    def splitPath(self, respath):
        path = respath.split('/')
        res = path[-1]
        path = path[:len(path)-1]
        return (path, res)

    def writeProperty(self, respath, propertyname, propertyns, propertyvalue):
        raise HTTPRequestException(processrequesterrorhandler.HTTP_CONFLICT)

    def removeProperty(self, respath, propertyname, propertyns):
        raise HTTPRequestException(processrequesterrorhandler.HTTP_CONFLICT)

    def getProperty(self, respath, propertyname, propertyns):
        at_sixth_nov = 1194341709.03
        at_eigth_nov = 1194513353.316092
        if propertyns == 'DAV:':
            isfile = self.isResource(respath)
            if propertyname == 'creationdate':
                return time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(at_sixth_nov))
            elif propertyname == 'getcontenttype':
                return self.getContentType(respath)
            elif propertyname == 'resourcetype':
                if self.isCollection(respath):
                    return '<D:collection />'            
                else:
                    return ''
            elif propertyname == 'getlastmodified':
                return time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(at_eigth_nov))
            elif propertyname == 'getcontentlength':
                if isfile:
                    db = DB(self.dbfile)
                    respath = respath.strip('/')
                    path = respath.split('/')
                    col = path[0]
                    path = path[1:len(path)]
                    path = '/'.join(path)
                    length = len(db.get(col, path))
                    db.close()
                    return str(length)
                raise HTTPRequestException(pyfileserver.processrequesterrorhandler.HTTP_NOT_FOUND)
            elif propertyname == 'getetag':
                return self.getEntityTag(respath)
        raise HTTPRequestException(pyfileserver.processrequesterrorhandler.HTTP_NOT_FOUND)

    def isPropertySupported(self, respath, propertyname, propertyns):
        supportedliveprops = ['creationdate', 'getcontenttype','resourcetype','getlastmodified', 'getcontentlength', 'getetag']
        if propertyns != "DAV:" or propertyname not in supportedliveprops:
            return False      
        return True


    def getSupportedPropertyNames(self, respath):
        appProps = []
        #DAV properties for all resources
        appProps.append( ('DAV:','creationdate') )
        appProps.append( ('DAV:','getcontenttype') )
        appProps.append( ('DAV:','resourcetype') )
        appProps.append( ('DAV:','getlastmodified') )   
        if os.path.isfile(respath):
            appProps.append( ('DAV:','getcontentlength') )
            appProps.append( ('DAV:','getetag') )
        return appProps


class Fusbuf(StringIO.StringIO):

    buf = ''
    path = ''
    closed = False

    def __init__(self, buffer, respath):
        if buffer is None:
            self.buf = ''
        else:
            self.buf = buffer
        self.path = respath
        self.closed = False

    def getvalue(self):
        return self.buf

    def write(self, value=''):
        db = DB('~/.fusion/fusion.db')

        if value is not None:
            self.buf = self.buf + value
        else:
            self.buf = self.buf + ''
        
        if self.buf is '' or self.path is '':
            db.close()
            return
        
        # path should be good at this point?
        
        pat = self.path.strip('/')

        pat = pat.split('/')
        col = pat[0]
        pat = pat[1:len(pat)]
        pat = '/'.join(pat)

        try:
            result = db.put(col, pat, self.buf)
        except Unauthorized:
            db.close()
            raise HTTPRequestException(processrequesterrorhandler.HTTP_FORBIDDEN)
        finally:
            db.close()

        db.close()

    def close(self):

        self.closed = True
